/*******************************************************************************
 * Copyright (c) 2014 Francesco Gabbrielli.
 * 
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the GNU Public License v2.0
 * which accompanies this distribution, and is available at
 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
 * 
 * Contributors:
 *     Francesco Gabbrielli - initial API and implementation
 ******************************************************************************/
package it.francescogabbrielli.android.solidalapp;

import java.io.IOException;

import it.francescogabbrielli.android.solidalapp.SolidalApp.DataKey;
import it.francescogabbrielli.android.solidalapp.deviceendpoint.Deviceendpoint;
import it.francescogabbrielli.android.solidalapp.deviceendpoint.model.Device;
import it.francescogabbrielli.android.solidalapp.profileendpoint.Profileendpoint;
import it.francescogabbrielli.android.solidalapp.profileendpoint.model.Profile;
import it.francescogabbrielli.android.solidalapp.requestendpoint.Requestendpoint;
import it.francescogabbrielli.android.solidalapp.requestendpoint.model.Request;
import it.francescogabbrielli.android.solidalapp.responseendpoint.Responseendpoint;
import it.francescogabbrielli.android.solidalapp.responseendpoint.model.Response;
import android.accounts.Account;
import android.accounts.AccountManager;
import android.app.IntentService;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.telephony.TelephonyManager;
import android.util.Log;

public class EndpointsService extends IntentService {

	private final static String TAG = SolidalApp.TAG + " Endpoints";

	private Profileendpoint profileEndpoint;
	private Deviceendpoint deviceEndpoint;
	private Requestendpoint requestEndpoint;
	private Responseendpoint responseEndpoint;

	private long lastUpdate;

	public EndpointsService() {
		super("Endpoints Service");
	}

	public Profileendpoint getProfileEndpoint() throws Exception {
		if (profileEndpoint == null)
			profileEndpoint = EndpointsUtils.buildEndpoint(Profileendpoint.class);
		return profileEndpoint;
	}

	public Deviceendpoint getDeviceEndpoint() throws Exception {
		if (deviceEndpoint == null)
			deviceEndpoint = EndpointsUtils.buildEndpoint(Deviceendpoint.class);
		return deviceEndpoint;
	}

	public Requestendpoint getRequestEndpoint() throws Exception {
		if (requestEndpoint == null)
			requestEndpoint = EndpointsUtils.buildEndpoint(Requestendpoint.class);
		return requestEndpoint;
	}
	
	public Responseendpoint getResponseEndpoint() throws Exception {
		if (responseEndpoint == null)
			responseEndpoint = EndpointsUtils.buildEndpoint(Responseendpoint.class);
		return responseEndpoint;
	}

	@Override
	protected void onHandleIntent(Intent intent) {
				
		SolidalApp app = (SolidalApp) getApplicationContext();
		SharedPreferences prefs = getSharedPreferences(SolidalApp.class.getCanonicalName(), MODE_PRIVATE);
		Intent localIntent = new Intent();

		try {
			
			switch(IntentAction.valueOf(intent)) {
			
			case ENDPOINTS_GET_PROFILE: {
				
				String id = prefs.getString(Constants.PROFILE_ACCOUNT, "");
				if("".equals(id)) {
					// find account (XXX: temporary)
					AccountManager manager = AccountManager.get(this);
					for(Account account : manager.getAccounts()) {
						Log.i("Account: "+account.type, account.name);
						if("com.google".equals(account.type)) {
							id = account.name;
							prefs.edit().putString(Constants.PROFILE_ACCOUNT, id).commit();
							break;
						}
					}
				}
				
				if(!"".equals(id))
					try {
						Profile profile = getProfileEndpoint().getProfileByAccount(id).execute();
						if (profile!=null)
							localIntent.putExtras(EndpointsUtils.toBundle(profile));
					} catch(Exception e) {
						localIntent.putExtra("error", "Errore server: "+e);
						Log.e(TAG, "Cannot read profile data for: "+id, e);
					}
				break;}
			
			case ENDPOINTS_INSERT_PROFILE: {
				
				// initial profile data
				Profile profile = new Profile();
				profile.setGoogleId(prefs.getString(Constants.PROFILE_ACCOUNT, ""));
				TelephonyManager tel = (TelephonyManager) getApplicationContext().getSystemService(TELEPHONY_SERVICE);
				profile.setDefaultPhone(tel.getLine1Number());
				profile.setCredits(10);
				
				try {
					Log.d(TAG, "Inserting new profile");
					getProfileEndpoint().insertProfile(profile).execute();
					localIntent.putExtras(EndpointsUtils.toBundle(profile));
				} catch(Exception e) {
					localIntent.putExtra("error", e.getMessage());
					Log.e(TAG, "Cannot insert profile data: "+profile, e);
				}
				
				break;}
				
			case ENDPOINTS_UPDATE_PROFILE: {
				
				Profile profile = (Profile) app.getData(DataKey.PROFILE);
				String id = prefs.getString(Constants.PROFILE_ACCOUNT, "");
				if(profile==null)
					break;
				
				// check if profile already exists (rare case)
				if("".equals(id))
					try {
						Profile temp = getProfileEndpoint().getProfileByAccount(profile.getGoogleId()).execute();
						if(temp!=null) {
							profile = temp;
							id = temp.getGoogleId();
							prefs.edit().putString(Constants.PROFILE_ACCOUNT, id).commit();
							app.setData(DataKey.PROFILE, profile);
						}
					} catch(Exception e) {
						localIntent.putExtra("error", e.getMessage());
						Log.e(TAG, "Cannot read profile data for: "+id, e);
					}
				
				// update profile data
				if("".equals(id))
					try {
						Log.w(TAG, "Inserting missing profile: "+profile);
						getProfileEndpoint().insertProfile(profile).execute();
						prefs.edit().putString(Constants.PROFILE_ACCOUNT, profile.getGoogleId()).commit();
					} catch(Exception e) {
						localIntent.putExtra("error", e.getMessage());
						Log.e(TAG, "Cannot insert profile data: "+profile, e);
					}
				else
					try {
						getProfileEndpoint().updateProfile(profile).execute();
					} catch(Exception e) {
						localIntent.putExtra("error", e.getMessage());
						Log.e(TAG, "Cannot update profile data: "+profile, e);
					}
				
				// update device if phone number changed...
				Device device = (Device) app.getData(DataKey.DEVICE);
				if(device!=null)
					try {
						getDeviceEndpoint().updateDevice(device).execute();
						Log.d(TAG, "Device updated: "+device);
					} catch(Exception e) {
						Log.e(TAG, "Error updating registered device "+device+": "+e, e);
					}
				
				break;}
			
			case ENDPOINTS_CHECK_DEVICE: {
				
//				if(System.currentTimeMillis() < lastUpdate + 5 * 60 * 1000l) {
//					Log.i(TAG, "Updating device later...");
//					return;
//				}
				
				String regId = prefs.getString(Constants.REGISTRATION_ID, "");
				
				if("".equals(regId)) {
					Log.e(TAG, "Cannot check device if not registered to GCM");
					return;
				}
						
				Device remoteDevice = null;

				//check first...
				try {
					remoteDevice = getDeviceEndpoint().getDevice(regId).execute();
					Log.d(TAG, "Device registered? "+regId+": "+remoteDevice);
				} catch(Exception e) {
					Log.e(TAG, "Error getting device "+regId+": "+e, e);
					break;
				}
				
				//...then insert or update
				if(remoteDevice==null)
					try {
						// try to insert
						Device newDevice = fillDevice(regId);
						remoteDevice = getDeviceEndpoint().insertDevice(newDevice).execute();
						Log.d(TAG, "Device inserted: "+remoteDevice.toPrettyString());
					} catch(Exception e) {
						Log.e(TAG, "Error registering device: "+e, e);
					}
				else
					try {
						// try to update
						fillDevice(remoteDevice);
						getDeviceEndpoint().updateDevice(remoteDevice).execute();
						Log.d(TAG, "Device updated: "+regId);
					} catch(Exception e) {
						Log.e(TAG, "Error updating registered device "+regId+": "+e, e);
					}
				
				if(remoteDevice!=null && remoteDevice.getError()==null) {
					localIntent.putExtras(EndpointsUtils.toBundle(remoteDevice));
					lastUpdate = System.currentTimeMillis();
					
					// check last request before returning
					if(app.getData(DataKey.REQUEST)==null) {
//						TODO: getRequestEndpoint().getLast()
					}
				}
				
				break;}
				
			case ENDPOINTS_SEND_REQUEST: {
				
				Request request = null;
				Request temp = EndpointsUtils.fromBundle(intent.getExtras(), Request.class);
				temp.setTimestamp(System.currentTimeMillis());
				
				try {
					request = getRequestEndpoint().insertRequest(temp).execute();
					prefs.edit().putLong(Constants.REQUEST_ID, request.getId()).commit();
					Log.d(TAG, "Request sent: "+temp.getMessage());
				} catch(Exception e) {
					localIntent.putExtra("error", e.toString());
					Log.e(TAG, "Error sending request: "+temp.getMessage(), e);
				}
				
				if(request!=null) {
					localIntent.putExtras(EndpointsUtils.toBundle(request));
					lastUpdate = System.currentTimeMillis();
				}
				
				break;}
			
			case ENDPOINTS_SEND_RESPONSE: {
				
				Response response = null;
				Response temp = EndpointsUtils.fromBundle(intent.getExtras(), Response.class);
				temp.setTimestamp(System.currentTimeMillis());
				
				try {
					response = getResponseEndpoint().insertResponse(temp).execute();
					Log.d(TAG, "Reponse sent: "+temp.getMessage());
				} catch(Exception e) {
					localIntent.putExtra("error", e.toString());
					Log.e(TAG, "Error sending response: "+temp.getMessage(), e);
				}
				
				if(response!=null) {
					localIntent.putExtras(EndpointsUtils.toBundle(response));
					lastUpdate = System.currentTimeMillis();
				}
				
				break;}
			
			case ENDPOINTS_SEND_FEEDBACK: {
				
				Response response = (Response) app.getData(DataKey.FILE_RESPONSE);
				
				try {

					int feedback = intent.getIntExtra(Constants.FEEDBACK, 0);
					if (feedback!=0 && !response.getCompleted()) {
						response.setFeedback(feedback);
						response = getResponseEndpoint().updateResponse(response).execute();
						if(response!=null) {
							localIntent.putExtras(EndpointsUtils.toBundle(response));
							Log.d(TAG, "Feedback sent: "+response);
						} else
							Log.w(TAG, "Feedback already sent?");
					}

				} catch(Exception e) {
					
					localIntent.putExtra("error", e.toString());
					Log.e(TAG, "Error sending feedback: "+response, e);
					
				}
				
				break;}
				
			case GCM_SEND_REQUEST_CHAT:
				try {
					Response res = (Response) app.getData(DataKey.FILE_RESPONSE);
					it.francescogabbrielli.android.solidalapp.requestendpoint.model.Message msg = 
							new it.francescogabbrielli.android.solidalapp.requestendpoint.model.Message();
					msg.setId(intent.getLongExtra("id", 0));
					msg.setRegistrationId(res.getRegistrationId());
					msg.setMessage(intent.getStringExtra("message"));
					msg = getRequestEndpoint().insertMessage(msg).execute();
					localIntent.putExtras(intent.getExtras());
					localIntent.putExtra("error", msg.getError());
				} catch(Exception e) {
					localIntent.putExtra("id", intent.getLongExtra("id", 0));
					localIntent.putExtra("error", e.toString());
					Log.e(TAG, "Error sending chat msg", e);
				}
				break;
				
			case GCM_SEND_RESPONSE_CHAT:
				try {
					Request req = (Request) app.getData(DataKey.FILE_REQUEST);
					it.francescogabbrielli.android.solidalapp.responseendpoint.model.Message msg = 
							new it.francescogabbrielli.android.solidalapp.responseendpoint.model.Message();
					msg.setId(intent.getLongExtra("id", 0));
					msg.setRegistrationId(req.getRegistrationId());
					msg.setMessage(intent.getStringExtra("message"));
					msg = getResponseEndpoint().insertMessage(msg).execute();
					localIntent.putExtras(intent.getExtras());
					localIntent.putExtra("error", msg.getError());
				} catch(Exception e) {
					Log.e(TAG, "Error sending chat msg", e);
					localIntent.putExtra("id", intent.getLongExtra("id", 0));
					localIntent.putExtra("error", e.toString());
				}
				break;
				
			default:
				Log.d(TAG, "Unexpected action: "+intent.getAction());
				
			}
			
		} catch(Exception e) {
			
			Log.e(TAG, "Unexpected error", e);
			localIntent.putExtra("error", "Unexpected error: "+e);
			
		} finally {
			
			localIntent.setAction(intent.getAction());
			Log.v(TAG, "broadcasting: "+localIntent+"\n"+localIntent.getExtras());
			sendBroadcast(localIntent);
			
		}
		
	}

	private Device fillDevice(Device device) throws Exception {
		
		device
			.setTimestamp(System.currentTimeMillis())
			.setInformation(
//					URLEncoder.encode(
							android.os.Build.MANUFACTURER + " " + android.os.Build.PRODUCT
//					,"UTF-8")
			);
		
		// XXX: Don't track location...
//		AppStatus status = ((SolidalApp) getApplicationContext()).getStatus();
//		Location location = status.getLocation();
//		if (location != null)
//			device.setLatitude(location.getLatitude()).setLongitude(
//					location.getLongitude());
		
		return device;
	}

	private Device fillDevice(String regId) throws Exception {
		Device device = new Device();
		device.setRegistrationId(regId);
		return fillDevice(device);
	}
	
	@Override
	public void onDestroy() {
		Log.d(TAG, "Destroy service");
		profileEndpoint = null;
		deviceEndpoint = null;
		requestEndpoint = null;
		responseEndpoint = null;
		super.onDestroy();
	}

}
